﻿$import( "SmitearWebGL.Core.ResourceItem" );
$import( "SmitearWebGL.Resources.Sampler" );

SmitearWebGL.Resources.Texture = SmitearWebGL.Core.ResourceItem.extend(
{
	/******** constructor ********/

	init : function()
	{
		this._super();
		this._registClass( "SmitearWebGL.Resources.Texture" );

		/******** public property ********/

		this.format = SmitearWebGL.Resources.Texture.Format.UNKNOWN_FORMAT;
		this.pixelStoreiType = SmitearWebGL.Resources.Texture.Type.UBYTE;
		this.levels = 1;
		this.usage = -1;

		/******** private property ********/

		this._glTexture = null;
		this._glTextureTarget = null;
		this._texWidth = 0;
		this._texHeight = 0;
		this._samplerBinded = false;
		this._sampleCache = null;
	}
	,

	/******** public method ********/
	
	getWidth : function()
	{
		return this._texWidth;
	}
	,
	getHeight : function()
	{
		return this._texHeight;
	}
	,
	getGlTexture : function()
	{
		return this._glTexture;
	}
	,
	getGlTextureTarget : function()
	{
		return this._glTextureTarget;
	}
	,
	glUniform : function( field, shader, id )
	{
		switch( field )
		{
			case SmitearWebGL.ShaderUniform.texture:
				if ( this._glTextureTarget == this.context.gl.TEXTURE_2D )
					this.context.gl.uniform1i( shader.uniforms["uSampler" + this.getResourceHandle()].location, id );
				else
					this.context.gl.uniform1i( shader.uniforms["uSamplerCube" + this.getResourceHandle()].location, id );
				break;
		}
	}
	,
	bindSampler : function( sampler )
	{
		$THROW( this.isResourceCreated(), "该资源还没有被创建." );

		this._samplerBinded = true;
		this._bindAndSetParameters( sampler.addressModeU, sampler.addressModeV, sampler.minFilter, sampler.magFilter );
		this._genMipmap();
	}
	,
	setFromBitmap : function( bitmap, face, level )
	{
		$THROW( this.isResourceCreated(), "该资源还没有被创建." );
		$THROW( bitmap.isResourceCreated(), "该资源还没有被创建." );
		
		this.setFromCanvas( bitmap.getCanvas(), bitmap.getFlip(), face, level );
	}
	,
	setFromCanvas : function( canvas, flip, face, level )
	{
		$THROW( this.isResourceCreated(), "该资源还没有被创建." );
		
		flip = flip || false;
		
		if ( !SmitearWebGL.Core.Math.isPowerOfTwo( canvas.width ) ||
			!SmitearWebGL.Core.Math.isPowerOfTwo( canvas.height ) )
		{
			var scratch = SmitearWebGL.Resources.Bitmap.createScratchCanvas(
			SmitearWebGL.Core.Math.nearestPowerOfTwo( canvas.width ),
			SmitearWebGL.Core.Math.nearestPowerOfTwo( canvas.height ) );
			
			scratch.getContext("2d").drawImage(canvas,
			0, 0, canvas.width, canvas.height,
			0, 0, scratch.width, scratch.height);
			canvas = scratch;
		}
		
		this.context.gl.bindTexture( this._glTextureTarget, this._glTexture );
		this.context.gl.pixelStorei( this.context.gl.UNPACK_FLIP_Y_WEBGL, flip );
		this.context.gl.texImage2D( this._getTexImage2DTarget( face ), level == undefined ? 0 : level, this.context.gl.RGBA, this.context.gl.RGBA, this.context.gl.UNSIGNED_BYTE, canvas );
		this.context.gl.pixelStorei( this.context.gl.UNPACK_FLIP_Y_WEBGL, 0 );

		this._texWidth = canvas.width;
		this._texHeight = canvas.height;
		
		this.context.gl.bindTexture( this._glTextureTarget, null );
	}
	,
	setFromCanvasEx : function( source_canvas, source_mip,
						source_x, source_y,
						source_width, source_height,
						dest_x, dest_y,
						dest_width, dest_height,
						flip, face, level )
	{
		$THROW( this.isResourceCreated(), "该资源还没有被创建." );
		
		dest_width = SmitearWebGL.Core.Math.nearestPowerOfTwo( dest_width );
		dest_height = SmitearWebGL.Core.Math.nearestPowerOfTwo( dest_height );
		
		var canvas = SmitearWebGL.Resources.Bitmap.createScratchCanvas( dest_width, dest_height );
		var context = canvas.getContext( '2d' );
		context.save();
		context.translate( - source_x, - source_y );
		context.scale( dest_width / source_width, dest_height / source_height );
		context.drawImage( source_canvas, 0, 0, source_canvas.width, source_canvas.height );

		this.context.gl.bindTexture( this._glTextureTarget, this._glTexture );
		this.context.gl.pixelStorei( this.context.gl.UNPACK_FLIP_Y_WEBGL, flip );
		this.context.gl.texImage2D( this._getTexImage2DTarget( face ), level == undefined ? 0 : level, this.context.gl.RGBA, this.context.gl.RGBA, this.context.gl.UNSIGNED_BYTE, canvas);
		this.context.gl.pixelStorei( this.context.gl.UNPACK_FLIP_Y_WEBGL, 0 );
		
		this._texWidth = canvas.width;
		this._texHeight = canvas.height;
		
		context.restore();
	}
	,
	createTexture : function( texWidth, texHeight, pixels, flip, intformat, format, type, face, level )
	{
		$THROW( this.isResourceCreated(), "该资源还没有被创建." );
		
		if ( ! texWidth || texWidth == 0 || ! texHeight || texHeight == 0 )
		{
			$THROW( false, "纹理宽度或高度未定义或为零" );
		}
		
		if ( !SmitearWebGL.Core.Math.isPowerOfTwo( texWidth ) ||
			!SmitearWebGL.Core.Math.isPowerOfTwo( texHeight ) )
		{
			texWidth = SmitearWebGL.Core.Math.nearestPowerOfTwo( texWidth );
			texHeight = SmitearWebGL.Core.Math.nearestPowerOfTwo( texHeight );
		}
		
		level = level == undefined ? 0 : level;
		intformat = intformat || this.format;
		format = format || SmitearWebGL.Resources.Texture.Format.ARGB8;
		type = type || this.pixelStoreiType;
		flip = flip || false;
		
		type = this._getGLTexturePixelType( type );
		intformat = this._getGLTextureFormat( intformat );
		format = this._getGLTextureFormat( format );
		
		this.context.gl.bindTexture( this._glTextureTarget, this._glTexture );
		this.context.gl.pixelStorei( this.context.gl.UNPACK_FLIP_Y_WEBGL, flip );
		if ( ! pixels )
			this.context.gl.texImage2D( this._getTexImage2DTarget( face ), level, intformat, texWidth, texHeight, 0, format, type, null );
		else
		{
			var glPixels = new Uint8Array( pixels.length );
			for ( var i = 0, n = texWidth * texHeight * 4; i < n; ++ i )
				glPixels[i] = pixels[i];
			this.context.gl.texImage2D( this._getTexImage2DTarget( face ), level, intformat, texWidth, texHeight, 0, format, type, glPixels );
		}
		this.context.gl.pixelStorei( this.context.gl.UNPACK_FLIP_Y_WEBGL, 0 );
		
		this._texWidth = texWidth;
		this._texHeight = texHeight;
		
		this.context.gl.bindTexture( this._glTextureTarget, null );
	}
	,
	
	/******** private method ********/

	_genMipmap : function()
	{
		$THROW( this.isResourceCreated(), "该资源还没有被创建." );
		
		if ( this._sampleCache.minFilter == this.context.gl.LINEAR_MIPMAP_LINEAR ||
			this._sampleCache.minFilter == this.context.gl.LINEAR_MIPMAP_NEAREST )
		{
			this.context.gl.bindTexture( this._glTextureTarget, this._glTexture );
			this.context.gl.generateMipmap( this._glTextureTarget );
			this.context.gl.bindTexture( this._glTextureTarget, null );
			this.levels = this._getMaxLevels( this._texWidth, this._texHeight );
		}
	}
	,
	_getGLTextureFormat : function( format )
	{
		switch ( format )
		{
			case SmitearWebGL.Resources.Texture.Format.XRGB8:
			return this.context.gl.RGB;

			case SmitearWebGL.Resources.Texture.Format.UNKNOWN_FORMAT:
			case SmitearWebGL.Resources.Texture.Format.ARGB8:
			case SmitearWebGL.Resources.Texture.Format.ABGR16F:
			case SmitearWebGL.Resources.Texture.Format.ABGR32F:
			return this.context.gl.RGBA;
	
			case SmitearWebGL.Resources.Texture.Format.R32F:
			case SmitearWebGL.Resources.Texture.Format.DXT1:
			case SmitearWebGL.Resources.Texture.Format.DXT3:
			case SmitearWebGL.Resources.Texture.Format.DXT5:
			default:
			return this.context.gl.RGBA;
		}
	}
	,
	_getGLTexturePixelType : function( type )
	{
		switch ( type )
		{
			case SmitearWebGL.Resources.Texture.Type.UBYTE:
			return this.context.gl.UNSIGNED_BYTE;
			
			case SmitearWebGL.Resources.Texture.Type.FLOAT32:
			return this.context.gl.FLOAT;
			
			case SmitearWebGL.Resources.Texture.Type.USHORT4444:
			return this.context.gl.UNSIGNED_SHORT_4_4_4_4;
			
			case SmitearWebGL.Resources.Texture.Type.USHORT5551:
			return this.context.gl.UNSIGNED_SHORT_5_5_5_1;
			
			case SmitearWebGL.Resources.Texture.Type.USHORT565:
			return this.context.gl.UNSIGNED_SHORT_5_6_5;
			
			default:
			return this.context.gl.UNSIGNED_BYTE;
		}
	}
	,
	_getTexImage2DTarget : function( face )
	{
		if ( this._glTextureTarget == this.context.gl.TEXTURE_CUBE_MAP )
		{
			return this.context.gl.TEXTURE_CUBE_MAP_POSITIVE_X + face;
		}
		else
		{
			return this.context.gl.TEXTURE_2D;
		}
	}
	,
	_getMaxLevels : function( width, height )
	{
		if (width == 0 || height == 0)
		{
			return 0;
		}
		var max = Math.max(width, height);
		var levels = 0;
		while (max > 0)
		{
			++levels;
			max = max >> 1;
		}
		return levels;
	}
	,
	_bindAndSetParameters : function( addressModeU, addressModeV, minFilter, magFilter )
	{
		if ( ! this._glTexture ) return;

		this.context.gl.bindTexture( this._glTextureTarget, this._glTexture );

		if ( this._sampleCache.addressModeU != addressModeU )
		{
			this.context.gl.texParameteri( this._glTextureTarget, this.context.gl.TEXTURE_WRAP_S, addressModeU );
			this._sampleCache.addressModeU = addressModeU;
		}
		if ( this._sampleCache.addressModeV != addressModeV )
		{
			this.context.gl.texParameteri( this._glTextureTarget, this.context.gl.TEXTURE_WRAP_T, addressModeV );
			this._sampleCache.addressModeV = addressModeV;
		}
		if ( this._sampleCache.minFilter != minFilter )
		{
			this.context.gl.texParameteri( this._glTextureTarget, this.context.gl.TEXTURE_MIN_FILTER, minFilter );
			this._sampleCache.minFilter = minFilter;
		}
		if ( this._sampleCache.magFilter != magFilter )
		{
			this.context.gl.texParameteri( this._glTextureTarget, this.context.gl.TEXTURE_MAG_FILTER, magFilter );
			this._sampleCache.magFilter = magFilter;
		}
		
		this.context.gl.bindTexture( this._glTextureTarget, null );
	}
	,

	/******** inherit method ********/

	_createResource : function()
	{
		this._super();

		this.format = SmitearWebGL.Resources.Texture.Format.UNKNOWN_FORMAT;
		this.pixelStoreiType = SmitearWebGL.Resources.Texture.Type.UBYTE;
		this.levels = 1;
		this.usage = -1;

		this._glTexture = this.context.gl.createTexture();
		this._glTextureTarget = this.context.gl.TEXTURE_2D;
		this._texWidth = 0;
		this._texHeight = 0;
		this._samplerBinded = false;
		this._sampleCache = 
		{
			addressModeU : this.context.gl.REPEAT,
			addressModeV : this.context.gl.REPEAT,
			minFilter : this.context.gl.LINEAR_MIPMAP_LINEAR,
			magFilter : this.context.gl.LINEAR
		}
	}
	,
	_destroyResource : function()
	{
		this._super();
		
		if ( this._glTexture )
			this.context.gl.deleteTexture( this._glTexture );

		this.format = SmitearWebGL.Resources.Texture.Format.UNKNOWN_FORMAT;
		this.pixelStoreiType = SmitearWebGL.Resources.Texture.Type.UBYTE;
		this.levels = 1;
		this.usage = -1;

		this._glTexture = null;
		this._glTextureTarget = null;
		this._texWidth = 0;
		this._texHeight = 0;
		this._samplerBinded = false;
		this._sampleCache = null;
	}
}
);

/******** static property ********/

SmitearWebGL.Resources.Texture.Format =
{
	UNKNOWN_FORMAT :	0,
	XRGB8 :				1,
	ARGB8 :				2,
	ABGR16F :			3,
	R32F :				4,
	ABGR32F :			5,
	DXT1 :				6,
	DXT3 :				7,
	DXT5 :				8
}

SmitearWebGL.Resources.Texture.Type =
{
	UBYTE :			0,
	FLOAT32 :		1,
	USHORT4444 :	2,
	USHORT5551 :	3,
	USHORT565 :		4
}

SmitearWebGL.Resources.Texture.LOAD_COMPLETE = "texture_load_complete";